implement PlainSocketImpl_L;

include "jni.m";
    jni : JNI;
        ClassModule,
        JString,
        JArray,
        JArrayI,
        JArrayC,
        JArrayB,
        JArrayS,
        JArrayJ,
        JArrayF,
        JArrayD,
        JArrayZ,
        JArrayJObject,
        JArrayJClass,
        JArrayJString,
        JClass,
        JObject : import jni;

#>> extra pre includes here
#<<

include "PlainSocketImpl_L.m";

#>> extra post includes here

include "jnet.m";
        jnet: JNET;

include "java/io/FileDescriptor_L.m";
	fildes: FileDescriptor_L;

include "InetAddress_L.m";
        inet: InetAddress_L;

include "srv.m";
        srv: Srv;


FileDescriptor_obj: import fildes;
InetAddress_obj: import inet;

# This next file was once generated by javal, now it must be hand crafted.
include "SocketImpl_L.m";
	sock: SocketImpl_L;

	sys: Sys;

	strp: String;

DEBUG: import jnet;
FD:    import sys;
SocketImpl_obj: import sock;
Value: import jni;

#<<

init( jni_p : JNI )
{
    # save java native inteface mod instance
    jni = jni_p;
    #>>extra initialization here

	sys = jni->sys;
	strp = jni->str;
	if((srv = load Srv Srv->BUILTINPATH) == nil
	&& (srv = load Srv Srv->PATH) == nil)
		jni->InitError( jni->sys->sprint( "java.net.PlainSocketImpl: could not load Srv: %r" ) );

    #<<
}

socketCreate_Z_V( this : ref PlainSocketImpl_obj, p0 : int)
{#>>

	cfd,fd: ref FD;
	dir,ctype: string;

	if (DEBUG)
		sys->print("Entering socketCreate\n");

	# Set socket ctype...
        if ( p0 ) {
		ctype = "tcp";
		this.ctype = jnet->SOCK_STREAM;
	} else {
		ctype = "udp";
		this.ctype = jnet->SOCK_DGRAM;
	}

	#Open clone device and allocate a connection
	dir = "/net/" + ctype + "/clone";
        cfd = sys->open(dir, sys->ORDWR);
	if (cfd == nil) {
		if( DEBUG )
			sys->print( "Unable to open clone device\n");
		jni->ThrowException( "java.net.SocketException",
			 "Unable to create socket");
	}
	this.cfd.fd = cfd;            

	buf := array[10] of byte;
        n := sys->read(cfd, buf, len buf);
	if (n < 0) {
		if (DEBUG)
			sys->print("socketCreate bad ctl file read\n");
		jni->ThrowException( "java.net.SocketException",
			 "socketCreate: Unable to create socket");
	}
	if (DEBUG)
		sys->print("socketCreate: ctl read connection #: %s\n", string buf[0:n]);
	s := string buf[:n];
	
	if (DEBUG)
		sys->print("socketCreate: s is %s\n", s );
	this.connection = int s;      

	# Populate data fd........
	dir = "/net/" + ctype + "/" + string this.connection  + "/data";
	if (DEBUG)
		sys->print("socketCreate: data dir: %s\n", dir);
       	fd = sys->open(dir, sys->ORDWR);

	if ( fd == nil ) {
		if (DEBUG) 
			sys->print("socketCreate: Attempt to open %s - no fd returned\n", dir);
		jni->ThrowException( "java.net.SocketException",
			 "Connect error: can't open connection");
	}

	this.fd.fd = fd;      
	
	if (DEBUG)
		sys->print("socketCreate: leaving \n");
}#<<

socketConnect_rInetAddress_I_V( this : ref PlainSocketImpl_obj, p0 : ref InetAddress_obj,p1 : int)
{#>>
	b:= array[4] of byte;
	buf:= array[50] of byte;
	port,str,ctype:  string;
	arg: list of string;
	i,n: int;
	fd: ref FD;
	
	if (DEBUG)
		sys->print("socketConnect: entering.. \n");
	# Get network ctype and address and port.......
	
        # Note p0 is an InetAddress_obj ref.....
	if ( p0 != nil ) {
		b[0]  =  byte (p0.address >> 24);
		b[1]  =  byte (p0.address >> 16);
		b[2]  =  byte (p0.address >> 8);
		b[3]  =  byte (p0.address);
		str = string b[0]+"."+string b[1]+"."+string b[2]+
			"."+string b[3];
		if( DEBUG ) {
			sys->print( "socketConnect: byte array = %s\n", str );
		}
	} else { 
		if( DEBUG )
			sys->print( "socketConnect: InetAddress ref = nil!\n");
		jni->ThrowException( "java.lang.NullPointerException",
			 "Null InetAddress");
	}

	if (this.ctype == jnet->SOCK_STREAM)
		ctype = "tcp";
	else
		ctype = "udp";

	
	# port is the third arg...
	if (p1>0) 
		port = string p1;
	else {
		if( DEBUG )
			sys->print( "socketConnect: No server port arg provided\n");
		jni->ThrowException( "java.net.SocketException",
			 "No port argument");
	}
	
	# Make string to write to ctl and issue connect....
	
	cstr := "connect " + str + "!" + port;
	if( DEBUG )
		sys->print( "socketConnect: connect str = %s\n", cstr);
	if ( this.cfd != nil ) 
		n = sys->write( this.cfd.fd, array of byte cstr, 
				len array of byte cstr );
	else { 
		if (DEBUG)
			sys->print("socketConnect: ctl fd == nil!\n");
		jni->ThrowException( "java.lang.NullPointerException",
			 "socketConnect: ctl file fd nil");
	}

	if( n <= 0 ) {
		if (DEBUG)
			sys->print( "Connect cmd to ctl file returned bad byte count %d\n",n);
		jni->ThrowException( "java.net.SocketException",
			 "Connect write error");
	}
	this.address = p0; 
        this.port = p1; 

	# Populate local port........

	if (this.localport == 0) {  		# If there was no bind
		cstr = "/net/"+ctype+"/"+string this.connection+"/local";
        	fd = sys->open(cstr, sys->OREAD);
		if ( fd == nil ) {
			if (DEBUG)
				sys->print("socketConnect: Open  %s failed.\n",
					 cstr );
			jni->ThrowException( "java.net.SocketException",
			 "Connect error");
		}
		n = sys->read( fd, buf, len buf );
		if ( n <= 0 ) { 
			if (DEBUG)
				sys->print("Connect Error: Read of %s returned %d\n", cstr ,n );
			jni->ThrowException( "java.net.SocketException",
			 "Connect error");
		}

		if (DEBUG)
			sys->print("socketConnect: Read of %s returned %s\n", 
				cstr, string buf[:n]);
		( i, arg) = sys->tokenize(string buf[0:n], "!"); 
		if (i == 0) {
			if (DEBUG)
				sys->print("Connect Error: tokenize of /local");
			jni->ThrowException( "java.net.SocketException",
			 "Connect error");
		}
		arg = tl arg;
		str = hd arg;
		if (DEBUG)
			sys->print("socketConnect: Tokenize for local port returned %s\n", str);
		this.localport = int str;  
	}
}#<<

socketBind_rInetAddress_I_V( this : ref PlainSocketImpl_obj, p0 : ref InetAddress_obj,p1 : int)
{#>>

	buf:= array[50] of byte;
	b := array[jnet->IPADDRLEN] of byte;
	i: int;
	arg: list of string;

	if (DEBUG)
		sys->print("Entering socketBind\n");

	# Create the localaddress/localport assoc to a socket......

	# Note p0 is a ref to InetAddress_obj 
        # The ip address was shifted in previously so...
	# IPV4 only....
        if ( p0 != nil  ) {
		if (DEBUG)
			sys->print( "socketBind: p0.address = %d\n",p0.address);
                b[0]  =  byte (p0.address >> 24);
                b[1]  =  byte (p0.address >> 16);
                b[2]  =  byte (p0.address >> 8);
                b[3]  =  byte (p0.address);
		x := string b[0]+"."+string b[1]+"."+string b[2]+
			"."+string b[3];
		if (DEBUG) 
			sys->print("socketBind: Bind using %s \n", x);
        } else
                jni->ThrowException( "java.lang.NullPointerException",
                         "Null InetAddress");

	if( this.fd == nil ) {
		if (DEBUG) 
			sys->print("Attempt to Bind using cfd = nil\n");
		jni->ThrowException("java.net.SocketException", "Null Socket");
	}	

	# bind  is announce
	
	if (p1)
        	str := "announce " +  string b[0] + "." + string b[1] +
		+ "." + string b[2] + "." +
		string b[3] + "!" + string p1;   
	else
        	str = "announce " +  string b[0] + "." + string b[1] +
		+ "." + string b[2] + "." + string b[3] ;   

	if( DEBUG )
		sys->print("socketBind: announce port is: %s\n", string p1);
		

        n := sys->write( this.cfd.fd, array of byte str, len array of byte str );

       	if( n <= 0 ) {
               	if (DEBUG)
                       	sys->print( "socketBind: %s to ctl failed\n", str);
                jni->ThrowException( "java.net.SocketException",
			"Bind error");
        }

        # Populate local port........
	if (this.ctype == jnet->SOCK_STREAM)
		ctype := "tcp";
	else
		ctype = "udp";

	cstr := "/net/"+ctype+"/"+string this.connection+"/local";
       	fd := sys->open(cstr, sys->OREAD);
	if ( fd == nil ) {
		if (DEBUG)
			sys->print("socketBind: Open  %s failed.\n",
				 cstr );
		jni->ThrowException( "java.net.SocketException",
		 	"Bind error");
	}
	n = sys->read( fd, buf, len buf );
	if ( n <= 0 ) { 
		if (DEBUG)
			sys->print("Bind Error: Read of %s returned %d\n", cstr ,n );
		jni->ThrowException( "java.net.SocketException",
			 "Bind error - opening /local");
	}

	if (DEBUG)
		sys->print("socketConnect: Read of %s returned %s\n", 
			cstr, string buf[:n]);
	( i, arg) = sys->tokenize(string buf[0:n], "!"); 
	if (i == 0) {
		if (DEBUG)
			sys->print("Bind Error: tokenize of /local");
		jni->ThrowException( "java.net.SocketException",
		 	"Bind error");
	}
	arg = tl arg;
	str = hd arg;
	if (DEBUG)
		sys->print("socketBind: Tokenize for local port returned %s\n", str);
	this.localport = int str;  
	this.address = p0;  
}#<<

socketListen_I_V( this : ref PlainSocketImpl_obj, p0 : int)
{#>>

	# Maybe should just return here max q is always 5......
      	if (DEBUG)
               	sys->print( "In socket listen routine\n");

}#<<

socketAccept_rSocketImpl_V( this : ref PlainSocketImpl_obj, p0 : ref  SocketImpl_obj )
{#>>
	# p0 is a ref to SocketImpl_obj 

        ctype,dir: string;
       	buf := array[200] of byte;
	str: string;
	st: list of string;
	m,n : int;

      	if (DEBUG)
               	sys->print( "In socketAccept\n");
	
	if ( p0 == nil ) {
		if( DEBUG )
			sys->print( "socketAccept: PlainSocketImpl ref = nil!\n");
		jni->ThrowException( "java.lang.NullPointerException",
			 "Null Socket Ptr");
	}

	if (this.ctype == jnet->SOCK_STREAM)
		ctype = "tcp";
	else
		ctype = "udp";

	if( this.timeout != 0 ) {
		temp:= jnet->timeout + " " + string this.timeout; 
		if (DEBUG) 
			sys->print("socketAccept: writing to ctl: %s\n", temp);
		if ( this.cfd != nil )
			rc := sys->write(this.cfd.fd, array of byte temp, 
				len array of byte temp); 
		else {
		        if (DEBUG) 
			        sys->print("socketAccept: ctl fd is nil\n");
                jni->ThrowException( "java.lang.NullPointerException",
                         "Null CTL FD");
		}
			
			
		if (DEBUG) 
			sys->print("socketAccept: wrote to ctl: %s\n", temp);
		if ( rc <= 0 ) { 
			if (DEBUG) 
				sys->print("socketAccept: Bad write to ctl: %s\n", temp);
			jni->ThrowException("java.net.SocketException", 
				"socketAccept: Unable to set timeout");
		}
	}
		
        dir = "/net/" + ctype + "/" + string this.connection + "/listen";
        if ((lfd := sys->open(dir, sys->ORDWR)) == nil ) {
                if (DEBUG) 
                        sys->print( "Open of listen file %s returned nil fd\n", dir);
                jni->ThrowException( "java.net.SocketException",
			 "socketAccept: accept failed");
        } 

	# Check status file to see if timed out...
        dir = "/net/" + ctype + "/" + string this.connection + "/status";
        if ((sfd := sys->open(dir, sys->OREAD)) == nil ) {
                if (DEBUG)
                        sys->print( "socketAccept: Open of status file %s returned nil fd\n", dir);
                jni->ThrowException( "java.net.SocketException",
                         "socketAccept: status file open failed");
        }

       	n = sys->read(sfd, buf, len buf);
	if ( n <= 0 ) {
		if (DEBUG)
			sys->print("socketAccept: status file read: %d\n", n);
		jni->ThrowException( "java.net.SocketException",
                         "socketAccept: status file read failed");
	}

        if (DEBUG)
                sys->print("socketAccept: Got status:  %s\n", string buf[0:n]);
	(m, st) = sys->tokenize( string buf[:n], " \t\n");

        for(i:=0;i<m;i++) {
                str = hd st;
		if ( strp->prefix( str, "timed-out" )) {	
               		if (DEBUG)
                       		sys->print( "socketAccept: Status is timeout\n");
               		jni->ThrowException("java.io.InterruptedIOException",
                         	"socketAccept: accept timed out.");
		}
		st = tl st;
	} 
	
	# Need to save the dfd and cfd. Note SocketImpl is abstract...
	if (DEBUG) 
            sys->print( "socketAccept: Storing fd from listen in SocketImpl_obj\n");
	# Get connection...
       	n = sys->read(lfd, buf, len buf);
       	if (n <= 0) {
                if (DEBUG)
                       	sys->print(" SocketAccept: ctl file read bad return code\n");
                jni->ThrowException( "java.net.SocketException",
			 "socketAccept: Unable to accept socket");
       	}

      	if (DEBUG)
               	sys->print("socketAccept: listen ctl conn #: %s\n", string buf[0:n]);

       	s := string buf[0:n];
      	if (DEBUG)
               	sys->print("socketAccept: storing connection number = %d in SocketImpl.\n", int s );
       	p0.connection = int s;     # save connection number
      	if (DEBUG)
               	sys->print("socketAccept: storing connection ctype = %d in SocketImpl.\n", this.ctype );
	p0.ctype = this.ctype;       # save connection ctype

        dir = "/net/" + ctype + "/" + string p0.connection + "/data";
	if ((fd := sys->open( dir, sys->ORDWR)) == nil ) {
        	if (DEBUG)
             		sys->print(" SocketAccept: open of %s failed\n", dir);

       		jni->ThrowException( "java.net.SocketException",
			 "Unable to open accepted socket");
	}
        if (DEBUG)
               	sys->print("SocketAccept: About to store dfd in SocketImpl fd (abstract class)\n");
       	p0.fd.fd = fd;         #Store data file fd.....

      	if (DEBUG)
               	sys->print(" socketAccept: storing accepted cfd now\n" );
       	p0.cfd.fd = lfd;             # store in class...

}#<<

socketAvailable_I( this : ref PlainSocketImpl_obj) : int
{#>>

      	if (DEBUG)
               	sys->print( "In socket available routine\n");
	jni->ThrowException( "java.io.IOException", "socketAvailable: Unsupported function" );
	return -1;

}#<<

socketClose_V( this : ref PlainSocketImpl_obj)
{#>>
      	if (DEBUG)
               	sys->print( "In socket close routine\n");
	this.fd = nil;
	this.cfd = nil;
	this = nil;

}#<<

initProto_V( )
{#>>
      	if (DEBUG)
               	sys->print( "In initProto routine\n");
	return;
}#<<

socketSetOption_I_Z_rObject_V( this : ref PlainSocketImpl_obj, p0 : int,p1 : int,p2 : JObject)
{#>>
	# p0 = opt
	# p1 = enable
	# p2 = val

      	if (DEBUG)
               	sys->print( "In SocketSetOption routine");

        if ( this.cfd == nil ) {
                if (DEBUG)
                        sys->print("PlainSocketImplSetOption: Null this.fd\n");
                jni->ThrowException("java.net.SocketException", "Socket Closed");
        }

        if (this.ctype == jnet->SOCK_STREAM)
                ctype := "tcp";
        else
                ctype = "udp";

	if( p0 == jnet->TCP_NODELAY )
		s := "tcp_nodelay " + string p1;	
	else if ( p0 == jnet->SO_LINGER )
		if ( p1 > 0 ) {
			if( p2 != nil ) {
				v := jni->GetObjField( p2, "value", "I");
				vl := v.Int();
				s = "linger " + string vl;
			}
		} else 
			s = "linger " + "-1";	
	else
		 jni->ThrowException( "java.net.SocketException", 
			"Socket Option Unsupported");

	if ( DEBUG )
		sys->print("SocketSetOpt: Option given is: %s\n", s );

	n := sys->write( this.cfd.fd, array of byte s, len array of byte s );

	if( n <= 0 ) {
                if (DEBUG)
                        sys->print( "SetSockOpt: cmd to ctl file returned bad byte count %d\n",n);
                jni->ThrowException( "java.net.SocketException",
                         "SetSocketOption error");
        }
}#<<

socketGetOption_I_I( this : ref PlainSocketImpl_obj, p0 : int) : int
{#>>

	temp,ctype,dir: string;
	buf:= array[256] of byte;
	s,str: string;
	iplist,st:  list of string;
	m,n,i : int;

	# p0 == socket option

      	if (DEBUG)
               	sys->print( "In SocketGetOption routine");

	if ( this.fd == nil ) {
		if (DEBUG)
			sys->print("PlainSocketImplGetOption: this.fd is null\n");
		jni->ThrowException("java.net.SocketException", "Socket Closed");
	}


	if (this.ctype == jnet->SOCK_STREAM)
                ctype = "tcp";
        else
                ctype = "udp";

	if (p0 == jnet->SO_LINGER )
		s = "linger";
	else if ( p0 == jnet->TCP_NODELAY )
		s = "tcp_nodelay";
	else if ( p0 == jnet->SO_BINADDR ){
        	sfd := sys->open("/dev/sysname", sys->OREAD);
		if ( sfd == nil ) {
                	jni->ThrowException( "java.net.SocketException",
                         "socketGetOpt: Inferno sysname file open failed");
		}
		
		if(( m = sys->read(sfd, buf, len buf)) < 0 ) {
			if (DEBUG) 
				sys->print("SocketGetOpt: SO_BINADDR: /dev/sysname read failed\n");
			return -1;
		}
		st = srv->iph2a(string buf[0:m]);
        	if (st == nil) { 
			if (DEBUG)
				sys->print("sockGetOpt: iph2a returned nil list\n");
	        	jni->ThrowException( "java.net.SocketException",
                        "socketGetOption: SO_BINADDR: Unable to get local address");
		}
                str = hd st;
               	if (DEBUG)
                       	sys->print("socketGetOption: st hd = %s\n", str);
               	# insure net byte ordered byte[]
               	( i, iplist ) = sys->tokenize( str, ".");
		if ( i == 0 ) {
			if (DEBUG)
				sys->print("sockGetOpt: tokenize 0 size list\n");
	        	jni->ThrowException( "java.net.SocketException",
                        "socketGetOption: SO_BINADDR: Unable tokenize local address");
		}
       		b:= array[len iplist] of byte;
               	for (j:=0; j<len iplist; j++) {
                       	temp = hd iplist;
			if (DEBUG )
				sys->print("socketGetOpt: temp = %s\n", temp);
                       	iplist = tl iplist;
                       	b[j] = byte temp;
		}
		i=0;
		i |= int ( b[0] << 24) ;	
		i |= int ( b[1] << 16 );
		i |= int ( b[2] << 8 );
		i |= int ( b[3] );
		if (DEBUG )
			sys->print("socketGetOpt: returning BINADDR = %d\n", i);
		return i;
	} else
		jni->ThrowException( "java.net.SocketException",
                "socketGetOption: Unknown Option");


        # Get status file string ...
        dir = "/net/" + ctype + "/" + string this.connection + "/status";

        if ((sfd := sys->open( dir, sys->OREAD )) == nil ) {
                if (DEBUG)
                        sys->print("socketGetOpt: Open status file %s return"+
				"ed nil fd\n", dir);
                jni->ThrowException( "java.net.SocketException",
                         "socketGetOpt: Inferno Status file open failed");
        }

        n = sys->read(sfd, buf, len buf);
        if ( n <= 0 ) {
                if( DEBUG )
                        sys->print("socketGetOpt: status file bad read %d\n", n);
                return -1;
        }
        if (DEBUG)
                sys->print("socketGetOpt: status file has: %s\n", string buf[0:n]);

        (m, st) = sys->tokenize( string buf[:n], " \t\n");
	if ( m == 0 )
		jni->ThrowException( "java.net.SocketException",
                "socketGetOption: Unable to tokenize status info");

       for(i=0;i<m;i++) {
                str = hd st;
                st = tl st;
                if ( strp->prefix( str, s )) {        
       			if (p0 == jnet -> SO_LINGER)
					return int hd st; 
       			else if ( p0 == jnet -> TCP_NODELAY)
					if ( int hd st != 0 )
						return 0;
					else
						break;
                }
       } 

       return -1;
}#<<
